November 2, 2026research

Hunt writable Docker host-root mounts without relying on privileged

A mount-level investigation path from recent Docker API honeypot inputs, with a tested local request-body filter.

#docker#containers#bind-mounts#host-root#ssh#hunting

Correction ·

Revised 3 November 2026: tightened analysis and practical hunting guidance. Observations unchanged.

Watch the mount configuration

From 2026-10-26 through 2026-11-01 UTC, BlackDome retained 108 deduplicated Docker container-create input records requesting a writable bind of the host root filesystem. They contained 24 distinct input contents across 6 reporting sources. The normalized privileged value was false in 46 records and true in 62: a rule requiring true would not select those 46 false-valued records. The emulated Docker Engine API honeypot listens on port 2375, records submitted container-create configurations and returns an emulated response without running a container.

Why check the bind independently

The facade records Privileged as a boolean, and a missing client Privileged option is normalized to false. For this hunt, inspect the mount configuration independently instead of making a true privileged value a precondition.

MITRE ATT&CK T1611 describes mounting the host filesystem through a bind parameter as one container-to-host access mechanism. Primary-source document

A second pivot: the requested command

On 2026-10-27 UTC, 10 retained container-create input records across 5 reporting sources requested append-redirection to an SSH authorized-key path under their requested writable host-root mount. All 10 had a normalized privileged value of true. Inspecting retained command input alongside the mount gives an additional triage pivot for environments that keep those request fields.

Hunt locally, then check ownership

Apply this local predicate to parsed Linux Docker container-create request bodies in logs that retain HostConfig.Binds. It checks writable host-root bind configuration independently of the privileged flag.

def writable_host_root(body):
    if not isinstance(body, dict):
        return False
    host = body.get("HostConfig")
    if not isinstance(host, dict):
        return False
    binds = host.get("Binds")
    if not isinstance(binds, list):
        return False
    for bind in binds:
        if not isinstance(bind, str):
            continue
        source, _, tail = bind.partition(":")
        target, _, options = tail.partition(":")
        if (source == "/" and target.startswith("/")
                and ":" not in options and "ro" not in options.split(",")):
            return True
    return False

Check the API caller, workload owner, requested mount and approved change record before escalating a match. Where local logs retain command input, inspect it for an authorized-key append request beneath the mounted host root. Where host telemetry is available, correlate the request time with relevant file changes and process activity.