Skip to content

πŸ”΄ Red Team Audit β€” Medium: MCP Docker flag validation is advisory-only + tag-only image pinningΒ #209

@github-actions

Description

@github-actions

πŸ”΄ Red Team Security Audit

Audit focus: Category F (Supply Chain) + Category D (Credential/Privilege Exposure via MCP Docker config)
Severity: Medium

Findings

# Vulnerability Severity File(s) Exploitable?
1 Dangerous MCP Docker flags compile to warnings, not errors Medium src/compile/standalone.rs:679-685 Yes (requires write access to pipeline def)
2 --userns, --cgroupns, --device missing from DANGEROUS_DOCKER_FLAGS Medium src/compile/standalone.rs:796-806 Yes (no warning emitted at all)
3 MCPG + AWF Docker images pinned by tag only, no @sha256 digest Medium templates/base.yml:202-204, src/compile/common.rs:731-734 Theoretical (requires registry compromise)

Details

Finding 1: Dangerous MCP Docker flags compile as warnings, not errors

Description: The functions validate_docker_args() and validate_mount_source() detect dangerous Docker flags (--privileged, --cap-add, --security-opt, --network, --pid, -u) and sensitive mounts (docker socket, /etc, /root, etc.) but only emit eprintln! warnings. Compilation succeeds and the dangerous flags are serialized verbatim into the MCPG JSON config (McpgServerConfig.args / .mounts fields).

Attack vector: An author with write access to a pipeline definition (or a reviewer who doesn't inspect stderr warnings during CI compilation) can include:

mcp-servers:
  escape-tool:
    container: "node:20-slim"
    entrypoint: "node"
    entrypoint-args: ["server.js"]
    args: ["--privileged"]          # ← only a warning, not a compile error
    mounts: ["/:/host:rw"]          # ← only a warning, not a compile error
```

At runtime, MCPG spawns the MCP container as:
```
docker run --privileged -v /:/host:rw node:20-slim ...

The privileged container has full host access, defeating AWF's L7 network isolation.

Proof of concept:

  1. Add the above mcp-servers: block to any agent markdown file
  2. Run ado-aw compile agent.md β€” compilation succeeds with only a warning to stderr
  3. Deploy the pipeline
  4. The MCP container spawned at runtime has --privileged and a full host bind-mount

Impact: Container escape from the AWF sandbox. An attacker inside the MCP server can access the host filesystem, exfiltrate secrets, pivot to other containers, or disable network isolation.

Suggested fix: Change validate_docker_args and validate_mount_source to return Result<()> and bail! on dangerous flags instead of returning warnings. Fail compilation with a hard error for --privileged, --cap-add, and bind mounts of sensitive paths. Warnings can remain for lower-risk flags.


Finding 2: --userns, --cgroupns, --device missing from DANGEROUS_DOCKER_FLAGS

Description: The DANGEROUS_DOCKER_FLAGS constant covers --privileged, --cap-add, --security-opt, --pid, --network, --ipc, --user, -u, --add-host, --entrypoint. However it omits several flags that can be used to escape or weaken container isolation with no warning at all:

  • --userns host β€” disables user-namespace isolation, maps container root to host root
  • --cgroupns host β€” shares the host cgroup namespace
  • --device /dev/mem or --device /dev/kmem β€” direct hardware access
  • --cgroup-parent β€” places container in an arbitrary host cgroup

Proof of concept:

args: ["--userns", "host"]   # No warning emitted during compilation

Impact: --userns host effectively gives the container root process the same UID 0 as the host, bypassing the user-namespace boundary that limits container escapes. Combining this with a writable mount of /proc or a device file could lead to host compromise.

Suggested fix: Add --userns, --cgroupns, --device, --cgroup-parent to DANGEROUS_DOCKER_FLAGS. Also change dangerous flag detection to fail compilation rather than warn (see Finding 1).


Finding 3: Docker images pinned by tag only (no digest)

Description: Three Docker images used in generated pipelines are referenced by tag only, not by immutable @sha256 digest:

Image Reference in code
ghcr.io/github/gh-aw-mcpg src/compile/common.rs:734, MCPG_VERSION = "0.2.19" β†’ :v0.2.19
ghcr.io/github/gh-aw-firewall/squid templates/base.yml:200 β†’ :\{\{ firewall_version }}
ghcr.io/github/gh-aw-firewall/agent templates/base.yml:201 β†’ :\{\{ firewall_version }}

If a tag is overwritten (accidentally or via a supply-chain compromise of ghcr.io/github), every pipeline compiled from that version of ado-aw would silently pull and execute the new (potentially malicious) image. Unlike the ado-aw and awf binaries (which are verified via sha256sum -c checksums.txt), Docker images have no integrity check beyond the tag.

Proof of concept: An attacker with write access to ghcr.io/github/gh-aw-mcpg pushes a malicious image tagged v0.2.19. All pipelines running docker pull ghcr.io/github/gh-aw-mcpg:v0.2.19 would get the malicious image.

Impact: Arbitrary code execution inside the CI runner as part of MCPG startup, with access to the Docker socket and host network.

Suggested fix: Pin images by digest in the compiled pipeline output:

# Instead of:
docker pull ghcr.io/github/gh-aw-mcpg:v0.2.19
# Use:
docker pull ghcr.io/github/gh-aw-mcpg@sha256:<pinned-digest>

The MCPG_VERSION/MCPG_IMAGE constants in common.rs can be extended with a corresponding MCPG_DIGEST constant. At a minimum, a docker inspect after pull should verify the expected digest.


Audit Coverage

Category Status
A: Input Sanitization & Injection βœ… Scanned β€” all prior findings fixed
B: Path Traversal & File System βœ… Scanned β€” no vulnerabilities found
C: Network & Domain Allowlist Bypass βœ… Scanned β€” all prior findings fixed
D: Credential & Secret Exposure βœ… Scanned β€” token handling correct; MCP flag issues above
E: Logic & Authorization Flaws βœ… Scanned β€” budget/allowlist enforcement correct
F: Supply Chain & Dependency Integrity βœ… Scanned β€” binary checksums OK; Docker image pinning gap

This issue was created by the automated red team security auditor.

Generated by Red Team Security Auditor Β· ● 3.3M Β· β—·

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions