CL-0024: Host-code-execution capabilities (ALL, SYS_ADMIN, SYS_MODULE, SYS_RAWIO)¶
Severity: CRITICAL
Derivation (see severity model):
- Baseline: A — the attacker already has code execution in this container, as the workload uid
- Precondition: Direct for the three mechanical members —
SYS_MODULEloads a kernel module,SYS_RAWIOopens the raw I/O ports,ALLgrants the full set — each reaching host code execution through supported calls, no technique and no second defect.SYS_ADMINsits in this cell as a documented judgment call (its escape does need a technique — see below), not as a no-technique claim; both readings land at CRITICAL, so the cell is unaffected - Impact: Host
- Qualifier/modifier: none
- Derived: Direct × Host = CRITICAL
- Shipped: CRITICAL
- Scoping assumptions:
SYS_ADMINis placed here on consistency grounds rather than on a verified no-technique escape — see the note below. That affects which rule reports it, not what it derives:Technique × Hostis also CRITICAL. The other three are mechanical - Evidence:
_cl0011proves only thatcap_adddeposits the capability into the effective set — not that any member reaches host code execution. Those reaches (module loading viaSYS_MODULE, raw I/O port access viaSYS_RAWIO, the full set viaALL) are kernel properties reasoned from the capability model and Docker's default seccomp profile, not exercised by a check
What it detects¶
cap_add entries naming a capability whose grant is host code execution:
| Capability | Grants |
|---|---|
ALL |
every Linux capability — the capability half of privileged: true |
SYS_ADMIN |
mount filesystems, configure namespaces; the historical home of most container-escape techniques |
SYS_MODULE |
load and unload kernel modules — arbitrary code in kernel space |
SYS_RAWIO |
raw I/O port access via iopl(2)/ioperm(2), granted by the capability alone. It also unlocks /dev/mem, though that path needs the device to be exposed as well and is bounded by CONFIG_STRICT_DEVMEM |
CAP_-prefixed and lowercase spellings are equivalent and are matched the same
way: Docker treats CAP_SYS_ADMIN and SYS_ADMIN as the same capability.
Why it matters¶
A container is not a security boundary against code running in the kernel.
SYS_MODULE loads a module; SYS_RAWIO opens the raw I/O ports; ALL grants
both and everything else. None of these needs a vulnerability — they are the
capability working as designed, granted to a workload that should not have it.
SYS_ADMIN is a judgment call about membership, not about severity.
Escaping with SYS_ADMIN does need a technique — mounting a cgroup hierarchy,
abusing a filesystem, or similar — so on a strict reading of this rule's
membership test (a capability reaching host code execution with no technique)
it does not qualify. Its severity does not turn on that question: the matrix
gives Technique × Host = CRITICAL exactly as it gives Direct × Host =
CRITICAL, so SYS_ADMIN derives CRITICAL on either reading of the
precondition axis.
It sits in this rule because it grants a superset of what the other three do in
practice, and because a user triaging findings is not served by SYS_MODULE
and SYS_ADMIN landing in different buckets. Grouping it here is a scale
choice, documented as one — but it is not the reason it is CRITICAL. Relegating
it to CL-0011 would score it HIGH only by also moving its impact axis to
Cross-container, and a SYS_ADMIN escape reaches the host.
Fix¶
Remove the capability. Unlike the tiers below it, this one has no
least-privilege reading — there is no subset of SYS_MODULE that is safe to
hold:
# Before
services:
app:
image: myapp:1.0
cap_add:
- SYS_ADMIN
# After — drop everything, add back only what the workload proves it needs
services:
app:
image: myapp:1.0
cap_drop:
- ALL
cap_add:
- CHOWN
If the workload genuinely requires one of these — a kernel-module loader, a storage tool that manages block devices — it needs a virtual machine or a host process, not a container. CL-0006 documents the drop-and-observe method for finding the capability set a service actually needs.
When to suppress¶
Effectively never. cap_add: [ALL] in particular has no legitimate use: it is
strictly worse than enumerating the capabilities a workload needs, and equal in
blast radius to privileged: true (CL-0002) on the capability
axis. If you must, suppress per service with a reason: naming the workload and
the isolation that contains it.
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.
| Technique | Tactic |
|---|---|
| T1611 Escape to Host | Privilege Escalation |