CL-0011: Strong host-adjacent capabilities (NETADMIN, BPF, SYSBOOT)

September 10, 2026 · View on GitHub

Severity: HIGH

Derivation (see severity model):

  • Baseline: A — the attacker already has code execution in this container, as the workload uid
  • Precondition: Technique — each member is a primitive that needs a published technique to become impact: ARP or route manipulation for NET_ADMIN, a BPF-based kernel attack, a reboot as a denial of service
  • Impact: Cross-container — traffic interception across containers on the host, kernel manipulation shared by all of them, or host availability
  • Qualifier/modifier: none
  • Derived: Technique × Cross-container = HIGH
  • Shipped: HIGH
  • Evidence: _cl0011 proves only that cap_add deposits the capability into the effective set — it does not exercise any member's reach. The cross-container impact (NET_ADMIN traffic manipulation on a shared Docker network, BPF kernel access) is reasoned from the capabilities' semantics under Docker's default seccomp profile; _t_net_admin covers only an in-netns route change, not cross-container interception. One member claim is empirically pinned: SYS_BOOT's kexec story is refuted, not assumed — kexec_load returns EPERM even with the capability held (captured on Docker 29.1.3), so the documented grant is reboot(2)

References:

What it detects

cap_add entries naming a capability that reaches past this container without handing over host code execution outright:

CapabilityGrants
NET_ADMINreconfigure interfaces, routes and firewall rules — transparent interception of other containers' traffic, which NET_RAW alone cannot reach
BPFload BPF programs — kernel introspection and manipulation, and a recurring container-escape surface
SYS_BOOTreboot or power off the host via reboot(2). It does not load a kernel via kexec: kexec_load(2) returns EPERM even with the capability held (verified on Docker 29.1.3) — the default seccomp profile blocks it outright rather than gating it on a capability

CAP_-prefixed and lowercase spellings are matched the same way: Docker treats CAP_NET_ADMIN and NET_ADMIN as the same capability.

This rule is one of six over cap_add, split by what the capability grants, because SARIF advertises security-severity on the rule descriptor and a rule carrying two severities misreports one of them:

RuleTierMembers
CL-0024CRITICALALL, SYS_ADMIN, SYS_MODULE, SYS_RAWIO
CL-0011HIGHNET_ADMIN, BPF, SYS_BOOT
CL-0028HIGHSYS_TIME, PERFMON
CL-0029HIGHSYS_NICE, IPC_LOCK, LEASE
CL-0030HIGHSYSLOG
CL-0027MEDIUMSYS_PTRACE, DAC_READ_SEARCH

Safe capabilities such as NET_BIND_SERVICE and CHOWN are not flagged by any of the six. Neither are MKNOD, SYS_CHROOT and DAC_OVERRIDE: all three are in Docker's default set, so flagging them on cap_add scored the declaration rather than the runtime state, and inverted the gate — cap_drop: [ALL] plus cap_add: [DAC_OVERRIDE] (one capability) failed at the default --fail-on high, while no cap_drop at all (fourteen capabilities, DAC_OVERRIDE among them) passed (issue #492).

Why it matters

Linux capabilities split root's privileges into discrete units, and these three are the ones that reach other containers or the host without being an escape on their own.

NET_ADMIN is the one to understand first, because it is the capability people add casually for VPN and networking containers. It is strictly more than NET_RAW (CL-0006): NET_RAW buys interception and denial of service against an L2 neighbour, while NET_ADMIN adds the routing and ip_forward control needed for a transparent man-in-the-middle that the victim cannot detect from its own connectivity.

BPF is a kernel read/write primitive on modern kernels and a recurring escape surface. SYS_BOOT is availability only — but host availability, which no other rule in the set reaches.

Fix

Remove the dangerous capability and evaluate whether the workload truly requires it:

# Before
services:
  app:
    image: myapp:1.0
    cap_add:
      - SYS_ADMIN

# After — drop all, add back only what's needed
services:
  app:
    image: myapp:1.0
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

If a dangerous capability is genuinely required (e.g., SYS_PTRACE for a debugger sidecar), document the justification and consider running the workload outside a container.

When to suppress

  • NET_ADMIN for VPN/networking containers (Tailscale, WireGuard, OpenVPN) — required to manage the tun device and routing tables. Suppress with a reason: naming the network workload.
  • SYS_PTRACE for a debugger or profiler sidecar that you trust and that runs alongside the workload it inspects.

Do not suppress cap_add: ALL — there is no legitimate use case for granting every capability rather than enumerating the specific ones needed.

ATT&CK coverage

Remediating this finding contributes to mitigating the following MITRE ATT&CK techniques (pinned to ATT&CK v18). compose-lint is a static analyser, so this is mitigation coverage — it detects nothing at runtime.

TechniqueTactic
T1611 Escape to HostPrivilege Escalation
T1557 Adversary-in-the-MiddleCredential Access (Enterprise/Linux, not on the Containers matrix)
T1040 Network SniffingCredential Access (Enterprise/Linux, not on the Containers matrix)
T1529 System Shutdown/RebootImpact

See also

  • CL-0002privileged: true (functional superset of cap_add: ALL)
  • CL-0006cap_drop: [ALL] baseline (drops the defaults this rule's cap_add re-grants)