Skip to content

CL-0030: Host-disclosure capability (SYSLOG)

Severity: HIGH

Derivation (see severity model):

  • Baseline: A — the attacker already has code execution in this container, as the workload uid
  • Precondition: Direct — syslog(2) alone returns the buffer. No published technique, no second defect, and no other key in the file
  • Impact: Host — the kernel ring buffer is a single host-wide log; it is not namespaced, so what the container reads is the host's
  • Qualifier/modifier: read-only — the realised loss is disclosure. The capability grants no write to the buffer and no execution anywhere, so the tier drops one from CRITICAL
  • Derived: Direct × Host + read-only = HIGH
  • Shipped: HIGH
  • Scoping assumptions: none. The reach needs nothing else in the file, and — measured — does not depend on the host's kernel.dmesg_restrict either (see Evidence)
  • Evidence: _cl0030_syslog in scripts/validate_rule_premises.py asserts the grant against a live daemon: dmesg returns no lines at --cap-drop ALL and returns the host's buffer with --cap-add SYSLOG. The check compares line counts, never content — the host's kernel log is not something a CI log should carry. Measured on Docker 29.4.3: 0 lines capless against 1,967 with the capability, on a host where kernel.dmesg_restrict=1 blocks an unprivileged host user from reading the same buffer. The independence from that sysctl was measured rather than assumed: with kernel.dmesg_restrict=0 a capless container still read 0 lines, because Docker's own default seccomp profile admits the syslog(2) syscall only for CAP_SYSLOG. The reach is therefore gated by the capability on any host, hardened or not

References: - OWASP Docker Security Rule #3 - CIS Docker Benchmark 5.4 — Ensure that Linux kernel capabilities are restricted within containers

What it detects

cap_add entries naming a capability that reads host state:

Capability Grants Why it is not CRITICAL
SYSLOG syslog(2) against the kernel ring buffer, and the ability to read it regardless of kernel.dmesg_restrict Pure observation. It writes nothing, runs nothing, and breaks nothing — the loss is confidentiality alone

CAP_-prefixed and lowercase spellings are equivalent and are matched the same way: Docker treats CAP_SYSLOG and SYSLOG as the same capability.

Why it matters

The kernel ring buffer is one log for the whole machine. A container holding SYSLOG reads the host's boot messages, its hardware and driver state, its filesystem and network events — and on a host that leaves kernel.kptr_restrict at 0, kernel pointers along with them, which is the address-layout information a local exploit wants.

It reads what the host itself hides. On a host with kernel.dmesg_restrict=1 — the hardened default on most distributions — an unprivileged user on the host cannot read that buffer. A container granted this one capability can. The container is not a smaller trust domain than the host user in that comparison; it is a larger one.

The grant does not depend on the host being hardened. Docker's default seccomp profile admits syslog(2) only when CAP_SYSLOG is held, so a capless container reads nothing even where kernel.dmesg_restrict=0 would let any host user read everything. That was measured, not assumed, and it is why this rule does not carry a "depends on host configuration" caveat.

Fix

Remove the capability. The kernel log belongs to the host, and so does reading it:

# Before
services:
  monitor:
    image: myapp:1.0
    cap_add:
      - SYSLOG

# After — collect kernel messages on the host, not from inside a container
services:
  monitor:
    image: myapp:1.0

Read the buffer on the host with journalctl -k, or run the log shipper there. A workload that needs kernel messages about its own device should be given that device explicitly — see CL-0016 — rather than the whole machine's log.

When to suppress

A host-level monitoring agent deliberately deployed as a container is the real case: it is doing the host's job, and reading the kernel log is the job. Such an agent usually announces itself with pid: host or a host bind mount as well. Suppress per service with a reason: naming the agent, rather than disabling the rule globally — the next service to ask for SYSLOG is unlikely to be a monitoring agent.

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
T1613 Container and Resource Discovery Discovery

See also

  • CL-0028 — the same Direct × Host cell, spending its qualifier on integrity-only
  • CL-0029 — the same cell again, spending it on availability-only
  • CL-0006 — dropping capabilities wholesale, the control this rule's findings evade