Skip to main content
RunBook Academy

LinuxXXX · Linux Capabilities and PrivilegeProcess capabilities

Root-equivalent capabilities - when dropping to one changes nothing

Advanced⏱ ~15 mincapshsystemd-analyze

What you'll learn

  • Identify the capabilities that are a direct path back to full root
  • Explain the escalation mechanism for each root-equivalent capability
  • Review a unit or container capability list for real privilege reduction
  • Recognise that dropping capabilities does not replace dropping the UID
  • Use capsh to research a capability before granting it

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11

Not yet marked complete on this device.

“We dropped everything except one capability” is the sentence a hardening review most often has to evaluate, and it carries no information on its own. Splitting root into forty pieces did not make forty small pieces. Several of them are the whole thing with an extra command in front.

This lesson is the catalogue: which capabilities are root by another name, by what mechanism, and how to review a grant rather than count it.

Three tiers

Tier 1 - root, immediately. Granting one of these is granting root. There is no scenario in which the grant is a reduction.

CapabilityThe step
CAP_SETUIDsetuid(0). That is the whole exploit.
CAP_SYS_MODULELoad a kernel module. The module runs in ring 0 and can do anything.
CAP_SYS_RAWIOOpen /dev/mem or use ioperm, then write kernel memory directly.
CAP_SYS_ADMINMount, pivot_root, namespace and keyring operations. So broad the man page has stopped trying to enumerate it.

Tier 2 - root after one obvious move. These require a command or two, all of which are documented behaviour rather than exploits.

CapabilityThe step
CAP_DAC_OVERRIDEBypass file permissions: write /etc/sudoers, /etc/shadow, or a unit file, then wait.
CAP_DAC_READ_SEARCHRead any file: SSH host keys, TLS private keys, /etc/shadow, application secrets. Also enables open_by_handle_at file-handle tricks.
CAP_CHOWNTake ownership of any file, then edit it under ordinary DAC rules.
CAP_FOWNERchmod files you do not own: make /etc/shadow world-writable, restore a setuid bit.
CAP_SETGIDJoin any group, including one that owns credentials or a socket that grants privilege.
CAP_SETFCAPWrite file capabilities onto a binary you control, then re-acquire anything still in the bounding set.
CAP_MKNODCreate a device node for the raw block device and read or write the filesystem directly, past every permission check the filesystem would apply.
CAP_SYS_PTRACEAttach to a root-owned process and inject code into it.
CAP_BPF, CAP_PERFMONAttach programs that read arbitrary kernel memory.
CAP_MAC_ADMIN, CAP_MAC_OVERRIDEChange or bypass the SELinux / Smack policy that was the compensating control.
CAP_LINUX_IMMUTABLEClear the immutable flag on the files somebody set it on precisely to stop this.

Tier 3 - genuinely narrower. Damaging in their own domain, but not a path to root.

CapabilityWhat it actually costs you
CAP_NET_BIND_SERVICEBind a low port. Impersonate a service on a port a client trusts.
CAP_NET_RAWCapture and forge packets on the segment. Serious, but it is a network problem, not a root problem.
CAP_NET_ADMINReconfigure interfaces, routes and firewall rules. Broad, and it takes the host firewall out of the picture.
CAP_SYS_TIMEMove the clock. Breaks TLS validity, Kerberos, log correlation and scheduled jobs.
CAP_SYS_BOOTReboot. An availability problem.
CAP_SYS_NICEStarve other processes of CPU.
CAP_IPC_LOCKPin memory. What a database wants.
CAP_KILLSignal any process. Stop things, not become things.
CAP_AUDIT_CONTROLTurn off auditing. Not escalation - it is the erasure of the record that escalation happened.
CAP_SYSLOGRead the kernel ring buffer, which leaks kernel addresses useful to a subsequent exploit.

Tier 3 is not “safe”. It is “the blast radius is describable”.

Two questions that classify a capability

You will meet capabilities not on any list, and kernel releases add more. The classification is mechanical if you ask two questions:

  1. Can it read a file the host treats as a credential? Any yes is a full compromise of everything that credential unlocks, which on a fleet host usually includes the ability to authenticate to other hosts.
  2. Can it cause root to execute something of my choosing? Writing a unit file, a cron entry, a sudoers line, a PATH-early binary, or a kernel module all qualify.

If either answer is yes, the capability is root-equivalent regardless of how narrow the man page description sounds.

capsh will tell you what a capability permits without a web search:

Read-only / SafeNote the second and third sentences, which the short description omits
$ capsh --explain=cap_setfcap
cap_setfcap (31) [/proc/self/status:CapXXX: 0x0000000080000000]

  Allows a process to set capabilities on files.
  Permits a process to uid_map the uid=0 of the
  parent user namespace into that of the child
  namespace. Also, permits a process to override
  securebits locks through user namespace
  creation.
# Find the capability that governs a thing you care about
capsh --suggest=module
capsh --suggest=immutable

Dropping capabilities is not dropping privilege

A process running as UID 0 with every capability dropped is still running as UID 0. Capabilities govern the checks the kernel makes in addition to the ordinary owner / group / other permission bits. They do not tighten those bits.

/etc/shadow on a Debian host is mode 0640, owner root, group shadow. A process with euid=0 and an empty capability set reads it as the owner. No capability was needed, because no permission check failed.

Read-only / SafeOwner root has read: no CAP_DAC_READ_SEARCH required
$ ls -l /etc/shadow
-rw-r----- 1 root shadow 1387 Aug  4 09:12 /etc/shadow

The ordering that follows from this:

  1. Run as a non-root user. This is the control that actually shrinks the reachable set of files.
  2. Then reduce the bounding set, so the non-root process cannot re-acquire privilege through a setuid binary or a file capability.
  3. Then set NoNewPrivileges=true, which closes those acquisition routes outright.

A unit that does step 2 and skips step 1 has hardened the smaller half.

[Service]
User=appsvc                                  # step 1
Group=appsvc
CapabilityBoundingSet=CAP_NET_BIND_SERVICE   # step 2
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true                         # step 3
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

Reviewing a grant

The review is four questions, in order, and it takes about a minute per service:

  1. Does it run as root? If yes, nothing below matters much until that is fixed or justified.
  2. Which tier is each granted capability in? A tier 1 or tier 2 entry needs a written reason, not a shrug.
  3. What is the evidence it is needed? “It failed without it” is evidence. “The upstream example had it” is not.
  4. Is the bounding set actually reduced? An AmbientCapabilities= line with no CapabilityBoundingSet= leaves the default bounding set in place.

Collect the current state across the units on a host:

# Every unit that keeps a non-default bounding set, and what it is
systemctl show '*.service' \
    --property=Id,User,CapabilityBoundingSet,AmbientCapabilities,NoNewPrivileges \
    --value --no-pager | head -40

# The scored version, one service at a time
systemd-analyze security nginx.service

systemd-analyze security gives an exposure score and names the directives that are unset. Treat the score as a prompt for the four questions above rather than as the answer - it does not know that your one remaining capability is CAP_SYS_ADMIN.

For containers, read the effective set rather than the run command, because an image can carry file capabilities of its own:

PID=$(docker inspect --format '{{.State.Pid}}' web)
sudo getpcaps "$PID"
capsh --decode="$(sudo awk '/^CapBnd:/ { print $2 }' /proc/"$PID"/status)"

Knowledge check

Knowledge check · 5 questions

  1. Q1. A container spec is `--cap-drop=ALL --cap-add=SYS_MODULE`. How much privilege has been removed?

  2. Q2. A service runs as root with CapabilityBoundingSet= reduced to CAP_NET_BIND_SERVICE. Can it read /etc/shadow?

  3. Q3. Which of these capabilities give a straightforward path back to full root? Select all that apply.

  4. Q4. CAP_AUDIT_CONTROL is not an escalation path; what it removes is the record that an action happened.

  5. Q5. Which pair of questions best classifies an unfamiliar capability as root-equivalent?

Passing score: 75%. Answers are checked in this browser.