Skip to content

mcp_setup_generator must restrict MCP gateway payload directory permissions — world-readable 0755 path exposes bearer token as d [Content truncated due to length] #31704

@szabta89

Description

@szabta89

Summary

mcp_setup_generator.go generates mkdir -p "\$\{MCP_GATEWAY_PAYLOAD_DIR}" for /tmp/gh-aw/mcp-payloads/ with no subsequent chmod, resulting in world-readable 0755 permissions. The MCP gateway container then mounts this path and creates a session subdirectory named after the full bearer token, making the token recoverable by any process on the runner host via a simple ls /tmp/gh-aw/mcp-payloads/ — no runner user identity required. This bypasses the intentional 0600 restriction on mcp-servers.json (hardened by prior fixes #1532, #1548, #1502) by leaking the same token through a different, unprotected filesystem path.

Affected Area

MCP gateway token confidentiality boundary — pkg/workflow/mcp_setup_generator.go (directory creation and permission setup for /tmp/gh-aw/mcp-payloads/); secondary concern in the MCP gateway container (ghcr.io/github/gh-aw-mcpg) which uses the raw token as the session directory name.

Reproduction Outline

  1. Run any gh-aw workflow that uses the MCP gateway (e.g., Claude Code engine), gh-aw v0.68.3.
  2. Wait approximately 90 seconds for the MCP gateway to process its first authenticated request.
  3. From any process on the runner host (any user identity — runner user NOT required), run:
    ls /tmp/gh-aw/mcp-payloads/
  4. The output is the full bearer token as a subdirectory name.
  5. Use the token directly for MCP gateway calls:
    TOKEN=$(ls /tmp/gh-aw/mcp-payloads/)
    curl -s -X POST <mcp-gateway-endpoint> \
      -H "Authorization: $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"x","version":"1"}}}'

Observed Behavior

/tmp/gh-aw/mcp-payloads/ is mode 0755 (world-readable). The gateway container (running as root) creates a subdirectory named after the full bearer token with mode 0755. The token is visible to any user on the host from a directory listing.

Expected Behavior

The mcp-payloads/ directory and its parent should be mode 0700 (runner-user-only), consistent with the intent behind mcp-servers.json being 0600. Additionally, the MCP gateway should use a hash or ephemeral random session ID as the directory name so the bearer token never appears as a filesystem path component.

Security Relevance

The bearer token controls access to create_issue, noop, and other safe-outputs write-sink tools via the MCP gateway. Recovery requires only a directory listing on a world-accessible path — no runner user identity, no special privileges. This is distinct from the runner-user-only risk in #25102 (upstream of githubnext/gh-aw-security#1695): that finding required the runner identity to read a 0600 file; this finding requires no special identity at all. Risk is highest on self-hosted or shared runners where other user identities (containers, services, tool processes) coexist on the host.

Suggested Fix

Immediate (smallest fix): Add chmod 700 "\$\{MCP_GATEWAY_PAYLOAD_DIR}" after the mkdir -p line in mcp_setup_generator.go (pkg/workflow/mcp_setup_generator.go, line ~575).

Deeper fix: Change the MCP gateway container to create session subdirectories named after a random session ID or one-way hash of the token — never the token itself — so the bearer token is never a filesystem path component.

Additional Context

If the current behavior (bearer token as directory name in a world-accessible path) is considered an acceptable operational assumption, that assumption should be explicitly documented in the security model. As-is, it contradicts the documented intent of the 0600 restriction on mcp-servers.json.

gh-aw version: v0.68.3 (from compiler_version in lock file)

Original finding: https://github.com/githubnext/gh-aw-security/issues/2125

Generated by File Issue · ● 244.5K ·

Metadata

Metadata

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