CVE-2026-54547
Meta Ads MCP: X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
Meta Ads MCP is a Model Context Protocol (MCP) server that lets AI assistants run Meta Ads. Prior to version 1.0.115, AuthInjectionMiddleware in meta_ads_mcp/core/http_auth_integration.py rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent, while extract_token_from_headers() does not recognize X-Pipeboard-Token as a primary credential. A network caller using the streamable-http transport can therefore send any X-Pipeboard-Token value, pass the guard without establishing authentication context, and cause get_auth_token() to fall back to the server operator's META_ACCESS_TOKEN. Subsequent MCP tools execute with the operator's Meta credentials and can read or modify the operator's Meta Ads data. Deployments using the default stdio transport or without META_ACCESS_TOKEN are not affected. This issue is fixed in version 1.0.115.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.4
Meta Ads MCP: X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
Meta Ads MCP is a Model Context Protocol (MCP) server that lets AI assistants run Meta Ads. Prior to version 1.0.115, AuthInjectionMiddleware in meta_ads_mcp/core/http_auth_integration.py rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent, while extract_token_from_headers() does not recognize X-Pipeboard-Token as a primary credential. A network caller using the streamable-http transport can therefore send any X-Pipeboard-Token value, pass the guard without establishing authentication context, and cause get_auth_token() to fall back to the server operator's META_ACCESS_TOKEN. Subsequent MCP tools execute with the operator's Meta credentials and can read or modify the operator's Meta Ads data. Deployments using the default stdio transport or without META_ACCESS_TOKEN are not affected. This issue is fixed in version 1.0.115.
- Record
CVE-2026-54547- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
EUVDEUVD-2026-78886
7.4
Meta Ads MCP is a Model Context Protocol (MCP) server that lets AI assistants run Meta Ads. Prior to version 1.0.115, AuthInjectionMiddleware in meta_ads_mcp/core/http_auth_integration.py rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent, while extract_token_from_headers() does not recognize X-Pipeboard-Token as a primary credential. A network caller using the streamable-http transport can therefore send any X-Pipeboard-Token value, pass the guard without establishing authentication context, and cause get_auth_token() to fall back to the server operator's META_ACCESS_TOKEN. Subsequent MCP tools execute with the operator's Meta credentials and can read or modify the operator's Meta Ads data. Deployments using the default stdio transport or without META_ACCESS_TOKEN are not affected. This issue is fixed in version 1.0.115.
- Record
EUVD-2026-78886- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-2v2f-mvfg-ph56
7.4
meta-ads-mcp: X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
Summary
AuthInjectionMiddleware in meta-ads-mcp rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent. Because extract_token_from_headers() does not recognise the X-Pipeboard-Token header, an attacker who sends that header with any arbitrary value produces auth_token = None and pipeboard_token = <attacker value>, making the guard condition evaluate to False and passing the request through. No authentication context is set; the token getter falls back to the server operator's META_ACCESS_TOKEN environment variable. Every subsequent MCP tool call executes with the operator's Meta credentials, allowing an unauthenticated network caller to read and write the operator's Meta Ads data.
Details
The vulnerable condition is at meta_ads_mcp/core/http_auth_integration.py:259:
# http_auth_integration.py:255-260
auth_token = FastMCPAuthIntegration.extract_token_from_headers(dict(request.headers))
pipeboard_token = FastMCPAuthIntegration.extract_pipeboard_token_from_headers(dict(request.headers))
if not auth_token and not pipeboard_token: # ← bypass condition
return Response(..., status_code=401)
extract_token_from_headers() (lines 77–95) recognises only Authorization: Bearer, X-META-ACCESS-TOKEN, and X-PIPEBOARD-API-TOKEN. It does not recognise X-Pipeboard-Token, so that header never populates auth_token.
extract_pipeboard_token_from_headers() (line 108) does recognise X-Pipeboard-Token, so sending that header alone produces:
auth_token = None # not set → guard reads False for left operand
pipeboard_token = "<anything>" # truthy → guard reads False for right operand
→ (not None) and (not "<anything>") = True and False = False → 401 never returned
After the bypass, set_auth_token() is never called (lines 283–291 only run when auth_token is truthy). The patched token getter at lines 163–168 resolves get_auth_token() = None, then delegates to original_get_current_access_token(). The fallback chain in auth.py:446–453 returns META_ACCESS_TOKEN from the server environment:
# auth.py:443-453
env_token = os.environ.get("META_ACCESS_TOKEN")
if env_token:
return env_token
@meta_api_tool at api.py:390–396 injects this operator token into every tool's access_token kwarg. The sink at api.py:225–235 forwards it to the Meta Graph API via httpx.AsyncClient. Verified with accounts.py:42–62 (get_ad_accounts): the operator's ad account data is returned for valid tokens; for invalid tokens the Meta Graph API responds with an OAuthException, confirming the token traversed the full path.
Full data flow:
| Step | Location | Description |
|------|----------|-------------|
| 1 | http_auth_integration.py:255–257 | Middleware extracts attacker-controlled headers |
| 2 | http_auth_integration.py:259 | Bypass: X-Pipeboard-Token alone satisfies guard |
| 3 | http_auth_integration.py:288–291 | auth_token is None; auth context never set |
| 4 | http_auth_integration.py:163–168 | Token getter falls back to original accessor |
| 5 | auth.py:446–453 | META_ACCESS_TOKEN env var returned as access token |
| 6 | api.py:390–396 | Operator token injected into tool kwargs |
| 7 | accounts.py:42–62 | Tool invokes Meta Graph API with operator token |
| 8 | api.py:225–235 | httpx.AsyncClient sends privileged HTTP request |
Recommended fix:
--- a/meta_ads_mcp/core/http_auth_integration.py
+++ b/meta_ads_mcp/core/http_auth_integration.py
- if not auth_token and not pipeboard_token:
+ if not auth_token:
X-Pipeboard-Token should be treated as a supplementary service token only; it must not serve as a standalone authentication credential for MCP tool calls.
PoC
Prerequisites
- Docker installed and the
meta-ads-mcp…
- Record
GHSA-2v2f-mvfg-ph56- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
NVD
7.4
Meta Ads MCP is a Model Context Protocol (MCP) server that lets AI assistants run Meta Ads. Prior to version 1.0.115, AuthInjectionMiddleware in meta_ads_mcp/core/http_auth_integration.py rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent, while extract_token_from_headers() does not recognize X-Pipeboard-Token as a primary credential. A network caller using the streamable-http transport can therefore send any X-Pipeboard-Token value, pass the guard without establishing authentication context, and cause get_auth_token() to fall back to the server operator's META_ACCESS_TOKEN. Subsequent MCP tools execute with the operator's Meta credentials and can read or modify the operator's Meta Ads data. Deployments using the default stdio transport or without META_ACCESS_TOKEN are not affected. This issue is fixed in version 1.0.115.
- Record
CVE-2026-54547- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSV
7.4
Meta Ads MCP: X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
Meta Ads MCP is a Model Context Protocol (MCP) server that lets AI assistants run Meta Ads. Prior to version 1.0.115, AuthInjectionMiddleware in meta_ads_mcp/core/http_auth_integration.py rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent, while extract_token_from_headers() does not recognize X-Pipeboard-Token as a primary credential. A network caller using the streamable-http transport can therefore send any X-Pipeboard-Token value, pass the guard without establishing authentication context, and cause get_auth_token() to fall back to the server operator's META_ACCESS_TOKEN. Subsequent MCP tools execute with the operator's Meta credentials and can read or modify the operator's Meta Ads data. Deployments using the default stdio transport or without META_ACCESS_TOKEN are not affected. This issue is fixed in version 1.0.115.
- Record
CVE-2026-54547- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSVGHSA-2v2f-mvfg-ph56
7.4
meta-ads-mcp: X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
Summary
AuthInjectionMiddleware in meta-ads-mcp rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent. Because extract_token_from_headers() does not recognise the X-Pipeboard-Token header, an attacker who sends that header with any arbitrary value produces auth_token = None and pipeboard_token = <attacker value>, making the guard condition evaluate to False and passing the request through. No authentication context is set; the token getter falls back to the server operator's META_ACCESS_TOKEN environment variable. Every subsequent MCP tool call executes with the operator's Meta credentials, allowing an unauthenticated network caller to read and write the operator's Meta Ads data.
Details
The vulnerable condition is at meta_ads_mcp/core/http_auth_integration.py:259:
# http_auth_integration.py:255-260
auth_token = FastMCPAuthIntegration.extract_token_from_headers(dict(request.headers))
pipeboard_token = FastMCPAuthIntegration.extract_pipeboard_token_from_headers(dict(request.headers))
if not auth_token and not pipeboard_token: # ← bypass condition
return Response(..., status_code=401)
extract_token_from_headers() (lines 77–95) recognises only Authorization: Bearer, X-META-ACCESS-TOKEN, and X-PIPEBOARD-API-TOKEN. It does not recognise X-Pipeboard-Token, so that header never populates auth_token.
extract_pipeboard_token_from_headers() (line 108) does recognise X-Pipeboard-Token, so sending that header alone produces:
auth_token = None # not set → guard reads False for left operand
pipeboard_token = "<anything>" # truthy → guard reads False for right operand
→ (not None) and (not "<anything>") = True and False = False → 401 never returned
After the bypass, set_auth_token() is never called (lines 283–291 only run when auth_token is truthy). The patched token getter at lines 163–168 resolves get_auth_token() = None, then delegates to original_get_current_access_token(). The fallback chain in auth.py:446–453 returns META_ACCESS_TOKEN from the server environment:
# auth.py:443-453
env_token = os.environ.get("META_ACCESS_TOKEN")
if env_token:
return env_token
@meta_api_tool at api.py:390–396 injects this operator token into every tool's access_token kwarg. The sink at api.py:225–235 forwards it to the Meta Graph API via httpx.AsyncClient. Verified with accounts.py:42–62 (get_ad_accounts): the operator's ad account data is returned for valid tokens; for invalid tokens the Meta Graph API responds with an OAuthException, confirming the token traversed the full path.
Full data flow:
| Step | Location | Description |
|------|----------|-------------|
| 1 | http_auth_integration.py:255–257 | Middleware extracts attacker-controlled headers |
| 2 | http_auth_integration.py:259 | Bypass: X-Pipeboard-Token alone satisfies guard |
| 3 | http_auth_integration.py:288–291 | auth_token is None; auth context never set |
| 4 | http_auth_integration.py:163–168 | Token getter falls back to original accessor |
| 5 | auth.py:446–453 | META_ACCESS_TOKEN env var returned as access token |
| 6 | api.py:390–396 | Operator token injected into tool kwargs |
| 7 | accounts.py:42–62 | Tool invokes Meta Graph API with operator token |
| 8 | api.py:225–235 | httpx.AsyncClient sends privileged HTTP request |
Recommended fix:
--- a/meta_ads_mcp/core/http_auth_integration.py
+++ b/meta_ads_mcp/core/http_auth_integration.py
- if not auth_token and not pipeboard_token:
+ if not auth_token:
X-Pipeboard-Token should be treated as a supplementary service token only; it must not serve as a standalone authentication credential for MCP tool calls.
PoC
Prerequisites
- Docker installed and the
meta-ads-mcp…
- Record
GHSA-2v2f-mvfg-ph56- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSVPYSEC-2026-3484
7.4
meta-ads-mcp: X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
X-Pipeboard-Token Header Auth Bypass Reuses Operator Meta Token
Summary
AuthInjectionMiddleware in meta-ads-mcp rejects HTTP MCP requests only when both auth_token and pipeboard_token are absent. Because extract_token_from_headers() does not recognise the X-Pipeboard-Token header, an attacker who sends that header with any arbitrary value produces auth_token = None and pipeboard_token = <attacker value>, making the guard condition evaluate to False and passing the request through. No authentication context is set; the token getter falls back to the server operator's META_ACCESS_TOKEN environment variable. Every subsequent MCP tool call executes with the operator's Meta credentials, allowing an unauthenticated network caller to read and write the operator's Meta Ads data.
Details
The vulnerable condition is at meta_ads_mcp/core/http_auth_integration.py:259:
# http_auth_integration.py:255-260
auth_token = FastMCPAuthIntegration.extract_token_from_headers(dict(request.headers))
pipeboard_token = FastMCPAuthIntegration.extract_pipeboard_token_from_headers(dict(request.headers))
if not auth_token and not pipeboard_token: # ← bypass condition
return Response(..., status_code=401)
extract_token_from_headers() (lines 77–95) recognises only Authorization: Bearer, X-META-ACCESS-TOKEN, and X-PIPEBOARD-API-TOKEN. It does not recognise X-Pipeboard-Token, so that header never populates auth_token.
extract_pipeboard_token_from_headers() (line 108) does recognise X-Pipeboard-Token, so sending that header alone produces:
auth_token = None # not set → guard reads False for left operand
pipeboard_token = "<anything>" # truthy → guard reads False for right operand
→ (not None) and (not "<anything>") = True and False = False → 401 never returned
After the bypass, set_auth_token() is never called (lines 283–291 only run when auth_token is truthy). The patched token getter at lines 163–168 resolves get_auth_token() = None, then delegates to original_get_current_access_token(). The fallback chain in auth.py:446–453 returns META_ACCESS_TOKEN from the server environment:
# auth.py:443-453
env_token = os.environ.get("META_ACCESS_TOKEN")
if env_token:
return env_token
@meta_api_tool at api.py:390–396 injects this operator token into every tool's access_token kwarg. The sink at api.py:225–235 forwards it to the Meta Graph API via httpx.AsyncClient. Verified with accounts.py:42–62 (get_ad_accounts): the operator's ad account data is returned for valid tokens; for invalid tokens the Meta Graph API responds with an OAuthException, confirming the token traversed the full path.
Full data flow:
| Step | Location | Description |
|------|----------|-------------|
| 1 | http_auth_integration.py:255–257 | Middleware extracts attacker-controlled headers |
| 2 | http_auth_integration.py:259 | Bypass: X-Pipeboard-Token alone satisfies guard |
| 3 | http_auth_integration.py:288–291 | auth_token is None; auth context never set |
| 4 | http_auth_integration.py:163–168 | Token getter falls back to original accessor |
| 5 | auth.py:446–453 | META_ACCESS_TOKEN env var returned as access token |
| 6 | api.py:390–396 | Operator token injected into tool kwargs |
| 7 | accounts.py:42–62 | Tool invokes Meta Graph API with operator token |
| 8 | api.py:225–235 | httpx.AsyncClient sends privileged HTTP request |
Recommended fix:
--- a/meta_ads_mcp/core/http_auth_integration.py
+++ b/meta_ads_mcp/core/http_auth_integration.py
- if not auth_token and not pipeboard_token:
+ if not auth_token:
X-Pipeboard-Token should be treated as a supplementary service token only; it must not serve as a standalone authentication credential for MCP tool calls.
PoC
Prerequisites
- Docker installed and the
meta-ads-mcp…
- Record
PYSEC-2026-3484- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
meta-ads-mcpPyPI |
before 1.0.115 | 1.0.115 |
meta-ads-mcpPyPI |
before 1.0.115, plus listed versions 0.1.00.10.00.10.10.10.20.10.5 Show 45 moreShow less0.10.60.10.70.10.90.11.00.11.10.11.20.11.30.11.40.11.50.11.60.2.00.2.10.2.20.2.30.2.40.2.50.2.60.2.80.2.90.3.00.3.10.3.100.3.20.3.30.3.50.3.60.3.70.3.80.3.90.4.00.4.10.4.20.4.30.4.40.4.50.4.60.4.70.4.80.4.90.5.00.6.00.7.00.7.10.7.100.7.2 |
1.0.115 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-54547 is a high vulnerability (CVSS 7.4) in Pipeboard-co Meta-ads-mcp, published July 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.52%, the 44th percentile. Fixed in meta-ads-mcp 1.0.115.
References
github.com/pipeboard-co/meta-ads-mcp/security/advisories/GHSA-2v2f-mvfg-ph56
github.com/pipeboard-co/meta-ads-mcp/pull/137
github.com/pipeboard-co/meta-ads-mcp/commit/95e852793b7ff8604a8132e85d5facd08c91a36e
github.com/pipeboard-co/meta-ads-mcp/releases/tag/1.0.115
github.com/advisories/GHSA-2v2f-mvfg-ph56
github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54547.json
nvd.nist.gov/vuln/detail/CVE-2026-54547
github.com/pipeboard-co/meta-ads-mcp
pypi.org/project/meta-ads-mcp
Left out here: 122 versions and the rest of 3 source descriptions. The JSON record has them.